package com.xiaoma.mvi

import android.annotation.SuppressLint
import android.graphics.PointF
import android.os.Bundle
import android.view.View
import android.view.ViewAnimationUtils
import android.view.ViewGroup
import androidx.annotation.ColorRes
import androidx.annotation.IdRes
import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.viewModels
import androidx.lifecycle.*
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.xiaoma.mvi.business.Dimens
import com.xiaoma.mvi.databinding.FragmentMainBinding
import com.xiaoma.mvi.databinding.ViewMainListItemBinding
import com.xiaoma.mvi.func.BindingFragment
import com.xiaoma.mvi.func.get
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlin.math.roundToInt

class MainFragment : BindingFragment<FragmentMainBinding>() {

    private val vm by viewModels<Vm>()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.prepareView()
    }

    private fun FragmentMainBinding.prepareView() {
        ViewCompat.setOnApplyWindowInsetsListener(rvList) { view, windowInsets ->
            val type = WindowInsetsCompat.Type.systemBars()
            val inset = windowInsets.getInsets(type)
            view.setPadding(
                inset.left + Dimens.viewPaddingHorizontal,
                inset.top + Dimens.viewPaddingTop,
                inset.right + Dimens.viewPaddingHorizontal,
                inset.bottom + Dimens.viewPaddingBottom
            )
            WindowInsetsCompat.CONSUMED
        }
        rvList.adapter = Adapter(
            vm.itemList,
            viewLifecycleOwner,
        ) { item, cx, cy ->
            binding.vMask.setBackgroundResource(item.color)
            ViewAnimationUtils.createCircularReveal(
                vMask,
                cx,
                cy,
                0F,
                root.height.coerceAtLeast(root.width).toFloat()
            ).run {
                doOnStart { vMask.visibility = View.VISIBLE }
                doOnEnd { findNavController().navigate(item.nodeId) }
                start()
            }
        }
    }
}

private class Diff(
    val oldList: List<MainItem>,
    val newList: List<MainItem>,
) : DiffUtil.Callback() {

    override fun getOldListSize() = oldList.size
    override fun getNewListSize() = newList.size

    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
        oldList[oldItemPosition].nodeId == newList[newItemPosition].nodeId

    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
        listOf(
            oldList[oldItemPosition].name == newList[newItemPosition].name,
            oldList[oldItemPosition].color == newList[newItemPosition].color,
        ).all { true }
}

private class Adapter(
    dataFlow: Flow<List<MainItem>>,
    private val lifecycleOwner: LifecycleOwner,
    private val onItemClick: (item: MainItem, x: Int, y: Int) -> Unit,
) : RecyclerView.Adapter<Holder>() {

    private var dataList: List<MainItem> = emptyList()
    private val dataListFlow = dataFlow.stateIn(
        lifecycleOwner.lifecycleScope,
        SharingStarted.Lazily,
        emptyList()
    )

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
        Holder(parent, lifecycleOwner, onItemClick)

    override fun onBindViewHolder(holder: Holder, position: Int) {
        holder.refresh(dataListFlow.value[position])
    }

    override fun getItemCount() = dataListFlow.value.size

    init {
        lifecycleOwner.lifecycleScope.launch {
            lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
                dataListFlow.collect { newList ->
                    val oldList = dataList
                    dataList = newList
                    DiffUtil.calculateDiff(Diff(oldList, newList))
                        .dispatchUpdatesTo(this@Adapter)

                }
            }
        }
    }
}

private class Holder(
    parent: ViewGroup,
    lifecycleOwner: LifecycleOwner,
    private val onItemClick: (item: MainItem, x: Int, y: Int) -> Unit,
) : ViewHolder(parent[R.layout.view_main_list_item]) {
    private val lastClickPoint = PointF(0F, 0F)

    private val binding = ViewMainListItemBinding.bind(itemView)
    private val bindingItem = MutableStateFlow<MainItem?>(null)

    private val name = bindingItem.map { it?.name }.distinctUntilChanged()

    fun refresh(item: MainItem?) = bindingItem.update { item }

    init {
        lifecycleOwner.lifecycleScope.collectState()
    }

    @SuppressLint("ClickableViewAccessibility")
    private fun LifecycleCoroutineScope.collectState() {
        launch {
            bindingItem.collect { item ->

                binding.root.setOnTouchListener { _, event ->
                    lastClickPoint.set(event.rawX, event.rawY)
                    false
                }

                binding.root.setOnClickListener {
                    item?.let {
                        onItemClick(
                            it,
                            lastClickPoint.x.roundToInt(),
                            lastClickPoint.y.roundToInt()
                        )
                    }
                }
            }
        }
        launch {
            name.collect { binding.tvName.text = it ?: "" }
        }
    }
}

data class MainItem(
    @IdRes
    val nodeId: Int,
    @ColorRes
    val color: Int,
    val name: String,
)

class Vm : ViewModel() {

    val itemList = MutableStateFlow(
        listOf(
            MainItem(
                R.id.fragmentRepeatOnLifecycle,
                android.R.color.holo_green_dark,
                "生命周期",
            ),
            MainItem(
                R.id.fragmentFlowAnim,
                android.R.color.holo_green_dark,
                "动画",
            ),
            MainItem(
                R.id.fragmentEnumSelection,
                android.R.color.holo_green_dark,
                "选中态(Enum)",
            ),
            MainItem(
                R.id.fragmentListSelection,
                android.R.color.holo_green_dark,
                "选中态(List)",
            ),
            MainItem(
                R.id.fragmentLoadingListSelection,
                android.R.color.holo_green_dark,
                "选中态(LoadingList)",
            ),


            )
    )

}